package bsh.dyn;

import java.math.BigDecimal;

import org.objectweb.asm.MethodVisitor;

import bsh.CompilationState;
import bsh.CompiledScript;
import bsh.exception.CompilationFailedException;
import bsh.meta.JavaClassWrapper;
import bsh.utils.reflection.transform.PrimitiveWideningTypeTransformations;

/**
 * {@link CompilableTransformation}
 * 
 * @author takeshi
 */
public class PrimitiveWideningTransformation implements CompilableTransformation {

    private final PrimitiveWideningTypeTransformations typeTransformations;

    /**
     * Ctor
     * 
     * @param typeTransformations the {@link PrimitiveWideningTypeTransformations}
     */
    public PrimitiveWideningTransformation(PrimitiveWideningTypeTransformations typeTransformations) {
        super();
        this.typeTransformations = typeTransformations;
    }

    /**
     * {@inheritDoc}
     */
    public int getWeight() {
        return 1;
    }

    /**
     * {@inheritDoc}
     */
    public ActualParameter getTransformationTarget() {
        Class<?> to = JavaClassWrapper.unwrapJavaClass(this.typeTransformations.getToType());
        return new PrimitiveParameter(to);
    }

    /**
     * {@inheritDoc}
     */
    public boolean acceptsType(ActualParameter parameter) {
        return parameter.isPrimitive()
                && JavaClassWrapper.unwrapJavaClass(this.typeTransformations.getFromType()).equals(parameter.getJavaType());
    }

    /**
     * {@inheritDoc}
     */
    public Object transform(Object object, CompiledScript script) {
        if (object instanceof Number) {
            Class<?> to = JavaClassWrapper.unwrapJavaClass(this.typeTransformations.getToType());
            if (to.equals(short.class)) {
                return ((Number) object).shortValue();
            } else if (to.equals(int.class)) {
                return ((Number) object).intValue();
            } else if (to.equals(long.class)) {
                return ((Number) object).longValue();
            } else if (to.equals(float.class)) {
                return ((Number) object).floatValue();
            } else if (to.equals(double.class)) {
                return ((Number) object).doubleValue();
            } else if (to.equals(BigDecimal.class)) {
                return null;
            }
        } else { // Character
            Character c = (Character) object;
            int toInt = c.charValue();
            return transform(toInt, script);
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isCompilable() {
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public void compile(MethodVisitor visitor, CompilationState state) throws CompilationFailedException {
        this.typeTransformations.transformStackTop(visitor, state.peekStack(), state);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.typeTransformations == null) ? 0 : this.typeTransformations.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        PrimitiveWideningTransformation other = (PrimitiveWideningTransformation) obj;
        if (this.typeTransformations == null) {
            if (other.typeTransformations != null) {
                return false;
            }
        } else if (!this.typeTransformations.equals(other.typeTransformations)) {
            return false;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    // @Override
    public String toString() {
        return "PrimitiveWideningTransformation [from: " + JavaClassWrapper.unwrapJavaClass(this.typeTransformations.getFromType())
                + " to: " + JavaClassWrapper.unwrapJavaClass(this.typeTransformations.getToType()) + "]";
    }

}